
// Copyright (C) 2007 Richard Uhler
// This file is part of Esiply.
//
// Esiply is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License, Version 2,
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

// This file contains the implementation for all the Number standard
// library stuff that can be implemented completely in javascript

Esiply.set_attributes(this, "Number", Esiply.DONT_ENUM);
Number = Esiply.create_janus_function(

// Ecma 15.7.1.1 Number(value)
// Returns a number value (not a Number object) computed by toNumber(value)
// if value was supplied, else returns +0.
function(value)
{
    return value == undefined ? +0 : +value;
},

// Ecma 15.7.2.1
// [[prototype]] is set to the number prototype
// [[class]] is set to "Number"
// [[value]] is set to toNumber(value) if value was supplied, else +0
function(value)
{
    var obj = new Object();
    Esiply.set_prototype(obj, Number.prototype);
    Esiply.set_class(obj, "Number");
    Esiply.set_value(obj, Number(value));
    return obj;
});

// Ecma 15.7.3.1 Number.prototype
// The inital value of Number.prototype is the Number prototype object
// This property has the attributes {DontEnum, DontDelete, ReadOnly}
Number.prototype = Esiply.number_prototype;
Esiply.set_attributes(Number, "prototype",
        Esiply.DONT_ENUM | Esiply.DONT_DELETE | Esiply.READ_ONLY);

// Ecma 15.7.3.2 Number.MAX_VALUE
// The value of Number.MAX_VALUE is the largest positive finite value of
// the number type, which is approximately 1.7976931348623157*10^308
// This property has the attributes {DontEnum, DontDelete, ReadOnly}
Number.MAX_VALUE = 1.7976931348623157e308;
Esiply.set_attributes(Number, "MAX_VALUE",
        Esiply.DONT_ENUM | Esiply.DONT_DELETE | Esiply.READ_ONLY);

// Ecma 15.7.3.3 Number.MIN_VALUE
// The value of Number.MIN_VALUE is the smallest positive value of the
// number type, which is approximately 5*10^-324
Number.MIN_VALUE = 5e-324;
Esiply.set_attributes(Number, "MIN_VALUE",
        Esiply.DONT_ENUM | Esiply.DONT_DELETE | Esiply.READ_ONLY);

// Ecma 15.7.3.4 Number.NaN
// The value of Number.NaN is NaN
Number.NaN = NaN;
Esiply.set_attributes(Number, "NaN",
        Esiply.DONT_ENUM | Esiply.DONT_DELETE | Esiply.READ_ONLY);

// Ecma 15.7.3.5 Number.NEGATIVE_INFINITY
// The value of Number.NEGATIVE_INFINITY is negative infinity
Number.NEGATIVE_INFINITY = -Infinity;
Esiply.set_attributes(Number, "NEGATIVE_INFINITY",
        Esiply.DONT_ENUM | Esiply.DONT_DELETE | Esiply.READ_ONLY);

// Ecma 15.7.3.6 Number.POSITIVE_INFINITY
// The value of Number.POSITIVE_INFINITY is + Infinity
Number.POSITIVE_INFINITY = Infinity;
Esiply.set_attributes(Number, "POSITIVE_INFINITY",
        Esiply.DONT_ENUM | Esiply.DONT_DELETE | Esiply.READ_ONLY);

// Ecma 15.7.4.1 Number.prototype.constructor
// The initial value of Number.prototype.constructor is the built-in
// Number constructor.
Esiply.set_attributes(Number.prototype, "constructor", Esiply.DONT_ENUM);
Number.prototype.constructor = Number;

// Ecma 15.7.4.2 Number.prototype.toString(radix)
// If radix is the number 10 or undefined, then this number value is 
// given as an argument to the toString operator; the resulting string
// value is returned.
// If radix is an integer from 2 to 36, but not 10, the result is a string,
// the choice of which is implementation-dependent
Esiply.set_attributes(Number.prototype, "toString", Esiply.DONT_ENUM);
Number.prototype.toString = function(radix)
{
    if (Esiply.get_class(this) != "Number")
    {
        throw new TypeError("Number.prototype.toString passed non-Number");
    }

    var num = this.valueOf();
    if (radix == 10 || radix == undefined)
    {
        return String(num);
    }

    radix = +radix;
    if (radix < 2 || radix > 36)
    {
        throw new RangeError("Number.prototype.toString radix out of range");
    }

    // TODO: finish this.
    var decval = num % 1;
    var intval = num - decval;
    throw new Error("Number.prototype.toString with not implemented yet for given radix");

};

// Ecma 15.7.4.4 Number.prototype.valueOf()
// Returns this number value. This is not generic. It throws
// a TypeError exception if tis this value is not a Number object.
Esiply.set_attributes(Number.prototype, "valueOf", Esiply.DONT_ENUM);
Number.prototype.valueOf = function()
{
    if (Esiply.get_class(this) != "Number")
    {
        throw new TypeError("Number.prototype.valueOf called on a non-number");
    }
    return Esiply.get_value(this);
};

// Ecma 15.7.4.5 Number.prototype.toFixed(fractionDigits)
// Return a string containing the number represented in fixed-point
// notation with fractionDigits digits after the decimal point.
// If fractionDigits is undefined, 0 is assumed.
Esiply.set_attributes(Number.prototype, "toFixed", Esiply.DONT_ENUM);
Number.prototype.toFixed = function(fractionDigits)
{
    // 1. convert fractionDigits to an integer
    var f = (fractionDigits == undefined)
                ? 0 : Math.round(Number(fractionDigits));

    // 2. If f < 0 or f > 20, throw a RangeError exception
    if (f < 0 || f > 20)
    {
        throw new RangeError("Bad range for fractionDigits in toFixed");
    }

    // 3. Let x be this number value
    var x = Number(this);

    // 4. If x is NaN, return the string "NaN"
    if (isNaN(x))
    {
        return "NaN";
    }

    // 5. Let s be the empty string.
    var s = "";

    // 6. if x >= 0 go to step 9
    if (x < 0)
    {
        // 7. let s be "-"
        s = "-";

        // 8. let x = -x;
        x = -x;
    }

    // 9. if x >= 10e21, let m = toString(x), and go to step 20
    var m = "";
    if (x >= 10e21)
    {
        m = x + "";

        // 20. return the concatenation of s and m
        return "" + s + m;
    }

    // 10. Let n be an integer for which the exact mathematical value of
    // n / 10^f -x is as close to zero as possible. If there are two such
    // n, pick the larger.  In other words, n = 10^f*x, but n has to be
    // an integer
    var n = Math.round(Math.pow(10, f)*x);

    // 11. If n = 0, let m be the string string "0". Otherwise, let m
    // be the string consisting of the digits of the decimal representaion
    // of n (in order, with no leading zeros)
    if (n == 0)
    {
        m = "0";
    }
    else
    {
        zero = new String("0");
        while (n > 0)
        {
            m = String.fromCharCode(n%10 + zero.charCodeAt(0)) + m;
            n = Math.floor(n/10);
        }
    }

    // 12. if f = 0, go to step 20
    if (f == 0)
    {
        return ("" + s) + m;
    }

    // 13. let k be the number of characters in m
    var k = m.length;

    // 14. if k > f, go to step 18
    if (k <= f)
    {
        // 15. let z be the string consisting of f+1-k occurrences of the
        // character 0
        var z = "";
        for (var i = 0; i < f+1-k; i++)
        {
            z += "0";
        }

        // 16. Let m be the concatenation of strings z and m
        m = z + m;

        // 17. let k = f + 1
        k = f+1;
    }

    // 18. let a be the first k-f characters of m, and let b be the
    // remaining f characters of m
    var a = m.substring(0, k-f);
    var b = m.substring(k-f, m.length);

    // 19. let m be a + "." + b
    // 20. return s + m
    return s + a + "." + b;
};

// Ecma 15.7.4.6 Number.prototype.toExponential(fractionDigits)
// TODO: implement this function
Esiply.set_attributes(Number.prototype, "toExponential", Esiply.DONT_ENUM);
Number.prototype.toExponential = function(fractionDigits)
{
    throw new Error("Number.prototype.toExponential: Not Yet Implemented");
};

// Ecma 15.7.4.7 Number.prototype.toPrecision(precision)
// TODO: implement this function
Esiply.set_attributes(Number.prototype, "toPrecision", Esiply.DONT_ENUM);
Number.prototype.toPrecision = function(precision)
{
    throw new Error("Number.prototype.toPrecision: Not Yet Implemented");
};

